#include <cstring>
#include <fstream>
#include <iostream>

#include "Arguments.h"
#include "XMLRawParser.h"

const std::string version= "0.1.0";
// ----------------------------------------------------------------------------

namespace gpxtools
{
  class GPXFetch : public XMLParserHandler
  {
  public:
    // -- Constructor -----------------------------------------------------------
    GPXFetch() :
      _arguments("gpxfetch [OPTION].. [FILE]\nFetch a waypoint, track or route from a GPX-file.\n", "gpxfetch v0.1", "Fetch a waypoint, track or route from a GPX-file and display the GPX-file with this waypoint, track or route on standard output."),
      _waypoint(_arguments, true,  'w', "waypoint",    "NAME", "fetch the waypoint with NAME", ""),
      _track   (_arguments, true,  't', "track",       "NAME", "fetch the track with NAME", ""),
      _route   (_arguments, true,  'r', "route",       "NAME", "fetch the route with NAME", ""),
      _xmlParser(this),
      _inWaypoint(false),
      _inRoute(false),
      _inTrack(false)
    {
    }

    // -- Deconstructor ---------------------------------------------------------
    virtual ~GPXFetch()
    {
    }

    // -- Properties ------------------------------------------------------------

    // -- Parse arguments -------------------------------------------------------
    bool processArguments(int argc, char *argv[])
    {
      std::vector<std::string> filenames;

      if (!_arguments.parse(argc,argv, filenames))
      {
        return false;
      }
      else if (!checkArguments(filenames))
      {
        return false;
      }
      else if (filenames.empty())
      {
        return _xmlParser.parse(std::cin);
      }
      else
      {
        return parseFile(filenames.front());
      }
    }

    // -- Check arguments ---------------------------------------------------------
    bool checkArguments(const std::vector<std::string> &filenames)
    {
      if (filenames.size() > 1)
      {
        std::cerr << "Too many input files."  << std::endl;
        return false;
      }

      return true;
    }

    // -- Parse a file ----------------------------------------------------------
    bool parseFile(const std::string &filename)
    {
      bool ok =false;

      std::ifstream file(filename);

      if (file.is_open())
      {
        ok = _xmlParser.parse(file);

        file.close();
      }
      else
      {
        std::cerr << "Unable to open: " << filename << std::endl;
      }

      return ok;
    }

  private:
    void store(const std::string &text)
    {
      if (_inWaypoint || _inRoute || _inTrack)
      {
        _currentText.append(text);
      }
      else
      {
        std::cout << text;
      }
    }

  public:
    // -- Callbacks -------------------------------------------------------------
    virtual void unhandled(const std::string &, const std::string &text)
    {
      store(text);
    }

    virtual void startElement(const std::string &path, const std::string &text, const std::string &, const Attributes &)
    {
      if (path == "/gpx/wpt")
      {
        _inWaypoint = true;
        _currentText.clear();
        _currentName.clear();
      }
      else if (path == "/gpx/rte")
      {
        _inRoute = true;
        _currentText.clear();
        _currentName.clear();
      }
      else if (path == "/gpx/trk")
      {
        _inTrack = true;
        _currentText.clear();
        _currentName.clear();
      }

      store(text);
    }

    virtual void text(const std::string &path, const std::string &text)
    {
      if (path == "/gpx/wpt/name" || path == "/gpx/rte/name" || path == "/gpx/trk/name")
      {
        _currentName.append(text);
      }

      store(text);
    }

    virtual void endElement(const std::string &path, const std::string &text, const std::string &)
    {
      store(text);

      XMLRawParser::trim(_currentName);

      if (path == "/gpx/wpt")
      {
        if (_inWaypoint && (_currentName == _waypoint.value()))
        {
          std::cout << _currentText;
        }

        _inWaypoint = false;
      }
      else if (path == "/gpx/rte")
      {
        if (_inRoute && (_currentName == _route.value()))
        {
          std::cout << _currentText;
        }

        _inRoute = false;
      }
      else if (path == "/gpx/trk")
      {
        if (_inTrack && (_currentName == _track.value()))
        {
          std::cout << _currentText;
        }

        _inTrack = false;
      }
    }

  private:
    // Members
    arg::Arguments  _arguments;
    arg::Argument   _waypoint;
    arg::Argument   _track;
    arg::Argument   _route;

    XMLRawParser    _xmlParser;

    bool          _inWaypoint;
    bool          _inRoute;
    bool          _inTrack;
    std::string   _currentText;
    std::string   _currentName;
  };
}

// -- Main program ------------------------------------------------------------

int main(int argc, char *argv[])
{
  gpxtools::GPXFetch gpxFetch;

  return gpxFetch.processArguments(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE;
}
